package com.coincalf.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.coincalf.dto.PositionSummaryDTO;
import com.coincalf.entity.*;
import com.coincalf.framework.dto.ForexMarketDTO;
import com.coincalf.framework.enums.OpenPositionOrderStatus;
import com.coincalf.framework.enums.OrderType;
import com.coincalf.framework.enums.PositionsDirection;
import com.coincalf.framework.enums.TradeType;
import com.coincalf.framework.exception.ForexAccountException;
import com.coincalf.mapper.ForexAccountMapper;
import com.coincalf.service.*;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 创新交易持仓信息 服务实现类
 * </p>
 *
 * @author qiang
 * @since 2018-05-12
 */
@Service
public class ForexAccountServiceImpl extends ServiceImpl<ForexAccountMapper, ForexAccount> implements ForexAccountService {

    @Autowired
    private ForexAccountDetailService forexAccountDetailService;
    @Autowired
    private MarketService marketService;
    @Autowired
    private ForexOpenPositionOrderService openPositionOrderService;
    @Autowired
    private TurnoverOrderService turnoverOrderService;

    /**
     * 查询持仓账户
     *
     * @param marketId  交易对ID
     * @param userId    用户ID
     * @param orderType 持仓方向
     * @return
     */
    public ForexAccount queryByUserAndMarketAndType(long marketId, long userId, OrderType orderType) {
        EntityWrapper<ForexAccount> wrapper = new EntityWrapper<>();
        wrapper.eq("user_id", userId)
                .eq("market_id", marketId)
                .eq("type", orderType.getCode());
        List<ForexAccount> forexAccounts = baseMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(forexAccounts)) {
            return null;
        }
        return forexAccounts.get(0);
    }

    /**
     * 冻结持仓
     *
     * @param accountId 持仓账户ID
     * @param volume    冻结金额
     * @param tradeType 交易类型
     * @param remark    备注
     * @return
     * @throws ForexAccountException
     */
    @Override
    public boolean lockAmount(long accountId,
                              BigDecimal volume,
                              TradeType tradeType,
                              String remark) throws ForexAccountException {
        if (baseMapper.lockAmount(accountId, volume) > 0) {
            ForexAccountDetail forexAccountDetail = this.generateForexAccountDetail(accountId, volume, tradeType, remark);
            forexAccountDetailService.insert(forexAccountDetail);
            return true;
        }
        throw new ForexAccountException("解冻持仓失败");
    }

    /**
     * 解冻持仓
     *
     * @param accountId 持仓账户ID
     * @param volume    冻结金额
     * @param tradeType 交易类型
     * @param remark    备注
     * @return
     * @throws ForexAccountException
     */
    @Override
    public boolean unlockAmount(long accountId,
                                BigDecimal volume,
                                TradeType tradeType,
                                String remark) throws ForexAccountException {
        if (baseMapper.unlockAmount(accountId, volume) > 0) {
            ForexAccountDetail forexAccountDetail = this.generateForexAccountDetail(accountId, volume, tradeType, remark);
            forexAccountDetailService.insert(forexAccountDetail);
            return true;
        }
        throw new ForexAccountException("解冻持仓失败");
    }

    /**
     * 持仓汇总
     *
     * @param userId 用户ID
     * @return
     */
    @Override
    public List<PositionSummaryDTO> positionSummary(Long userId) {
        // 品种、委托类型、数量、持仓均价、持仓盈亏、手续费、现价、开仓时间
        EntityWrapper<ForexAccount> wrapper = new EntityWrapper<>();
        wrapper.eq("user_id", userId)
                .andNew("amount > {0}", 0)
                .or().gt("lock_amount", 0);
        List<ForexAccount> forexAccounts = baseMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(forexAccounts)) {
            return new ArrayList<>();
        }
        List<PositionSummaryDTO> positionSummaryList = new ArrayList<>(forexAccounts.size());
        for (ForexAccount forexAccount : forexAccounts) {
            Market market = marketService.getMarketById(forexAccount.getMarketId());
            ForexMarketDTO forexMarket = marketService.getMarketDataById(forexAccount.getMarketId());
            PositionSummaryDTO positionSummary = new PositionSummaryDTO();
            positionSummaryList.add(positionSummary);
            positionSummary.setMarketName(forexAccount.getMarketName()) // 品种
                    .setMarketId(market.getId())                        // 市场ID
                    .setType(forexAccount.getType())                    // 委托类型
                    .setVolume(forexAccount.getAmount()
                            .add(forexAccount.getLockAmount()))         // 持仓量
                    .setCurrentPrice(forexMarket.getPrice());           // 当前价
            // 手续费
            BigDecimal fee = BigDecimal.ZERO;
            // 持仓盈亏
            BigDecimal profit = BigDecimal.ZERO;
            // 开仓时间
            Date openTime = null;
            // 开仓总额
            BigDecimal amount = BigDecimal.ZERO;
            // 开仓总量
            BigDecimal volume = BigDecimal.ZERO;
            EntityWrapper<ForexOpenPositionOrder> openPositionWrapper = new EntityWrapper<>();
            openPositionWrapper.eq("user_id", userId)
                    .eq("status", OpenPositionOrderStatus.OPEN.getCode())
                    .eq("market_id", forexAccount.getMarketId())
                    .eq("type", forexAccount.getType())
                    .orderBy("created", false);
            List<ForexOpenPositionOrder> openPositionOrderList = openPositionOrderService.selectList(openPositionWrapper);
            for (ForexOpenPositionOrder openPositionOrder : openPositionOrderList) {
                // 手续费
                EntityWrapper<TurnoverOrder> dealOrderWrapper = new EntityWrapper<>();
                dealOrderWrapper.eq("id", openPositionOrder.getOrderId());
                TurnoverOrder turnoverorder = turnoverOrderService.selectOne(dealOrderWrapper);
                if (forexAccount.getType().intValue() == PositionsDirection.BUY.getValue()) {
                    fee = fee.add(turnoverorder.getDealBuyFee());
                } else {
                    fee = fee.add(turnoverorder.getDealSellFee());
                }
                // 计算持仓盈亏
                BigDecimal opsitionProfit = calculateProfit(openPositionOrder, market, forexMarket);
                profit = profit.add(opsitionProfit);
                openTime = openPositionOrder.getCreated();
                amount =  amount.add(openPositionOrder.getNum().multiply(openPositionOrder.getPrice()));
                volume = volume.add(openPositionOrder.getNum());
            }
            // 持仓均价
            BigDecimal avgPrice = amount.divide(volume, market.getPriceScale(), BigDecimal.ROUND_HALF_UP);
            positionSummary.setFee(fee)
                    .setOpenTime(openTime)
                    .setProfit(profit.setScale(8, BigDecimal.ROUND_HALF_UP))
                    .setAvgPrice(avgPrice);
        }
        // 倒叙
        Collections.reverse(positionSummaryList);
        return positionSummaryList;
    }

    /**
     * 浮动盈亏计算
     * 多头持仓：（买一价 - 持仓买入价） * 合约单位 * 报价货币兑（USDT或BTC） * 手数
     * 空头持仓：（持仓卖出价 - 卖一价）  * 合约单位 * 报价货币兑（USDT或BTC） * 手数
     *
     * @param forexOpenPositionOrder
     * @param market
     * @param forexMarket
     * @return
     */
    @Override
    public BigDecimal calculateProfit(ForexOpenPositionOrder forexOpenPositionOrder, Market market, ForexMarketDTO forexMarket) {
        BigDecimal profit;
        if (forexOpenPositionOrder.getType() == 1) {
            // 多头持仓
            profit = (forexMarket.getBidPrice()
                    .subtract(forexOpenPositionOrder.getPrice()))
                    .multiply(BigDecimal.valueOf(market.getContractUnit()))
                    .multiply(marketService.getPriceToCoinRate(market))
                    .multiply((forexOpenPositionOrder.getNum().subtract(forexOpenPositionOrder.getCloseNum())));
        } else {
            //空头持仓
            profit = (forexOpenPositionOrder.getPrice()
                    .subtract(forexMarket.getAskPrice()))
                    .multiply(BigDecimal.valueOf(market.getContractUnit()))
                    .multiply(marketService.getPriceToCoinRate(market))
                    .multiply((forexOpenPositionOrder.getNum().subtract(forexOpenPositionOrder.getCloseNum())));
        }
        return profit;
    }

    /**
     * 构建持仓账户流水
     *
     * @param accountId 持仓账户ID
     * @param amount    持仓量
     * @param remark    备注
     * @return
     */
    private ForexAccountDetail generateForexAccountDetail(long accountId, BigDecimal amount, TradeType tradeType, String remark) {
        ForexAccountDetail forexAccountDetail = new ForexAccountDetail();
        forexAccountDetail.setAccountId(accountId)              // 账户ID
                .setAmount(amount)                              // 持仓量
                .setType(tradeType.getCode())                   // 类型
                .setRemark(remark);                             // 备注
        return forexAccountDetail;
    }

}
